{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e1c190df-3aac-46b6-ad15-2ef2733a509a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "\n",
    "from langchain.chat_models import init_chat_model\n",
    "from langchain_tavily import TavilySearch\n",
    "from langchain_core.messages import BaseMessage\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import StateGraph\n",
    "from langgraph.graph.message import add_messages\n",
    "from langgraph.prebuilt import ToolNode, tools_condition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7fc115da-6cc5-4df3-8396-7df0f188a0fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "assert load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b21e2e0-7fa2-4f8f-9747-222e88de5f42",
   "metadata": {},
   "source": [
    "- `Who won the 2025 Champions League?`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "04fc3a41-0dd6-4376-94d1-1be56d3eab2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = init_chat_model(\"openai:gpt-4o-mini\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "838e1c78-3f33-498b-bcdc-8d9fff7c8565",
   "metadata": {},
   "outputs": [],
   "source": [
    "class State(TypedDict):\n",
    "    # 在 graph node 间数据流转，不断追加 messages\n",
    "    # [human, ai(tool), ai, ...]\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "tool = TavilySearch(max_results=2)\n",
    "tools = [tool]\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "tool_node = ToolNode(tools=[tool])\n",
    "graph_builder.add_node(\"tools\", tool_node)\n",
    "\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"chatbot\",\n",
    "    tools_condition,\n",
    ")\n",
    "graph_builder.add_edge(\"tools\", \"chatbot\")\n",
    "graph_builder.set_entry_point(\"chatbot\")\n",
    "memory = MemorySaver()\n",
    "graph = graph_builder.compile(checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "eb18c96e-b627-4ec3-aa1f-9a2b7bc0f7f0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "98c27c5e-832d-4324-abbc-a26331d36a93",
   "metadata": {},
   "outputs": [],
   "source": [
    "import uuid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "d623751b-8467-44e3-b163-26ddb40d125e",
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyboardInterrupt",
     "evalue": "Interrupted by user",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[14], line 3\u001b[0m\n\u001b[1;32m      1\u001b[0m config \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m\"\u001b[39m: {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mthread_id\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mstr\u001b[39m(uuid\u001b[38;5;241m.\u001b[39muuid4())}}\n\u001b[1;32m      2\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m----> 3\u001b[0m     user_input \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43minput\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mUser: \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m      4\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m user_input\u001b[38;5;241m.\u001b[39mlower() \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mquit\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexit\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mq\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[1;32m      5\u001b[0m         \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGoodbye!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "File \u001b[0;32m~/ProgramFiles/anaconda3/envs/mm/lib/python3.10/site-packages/ipykernel/kernelbase.py:1282\u001b[0m, in \u001b[0;36mKernel.raw_input\u001b[0;34m(self, prompt)\u001b[0m\n\u001b[1;32m   1280\u001b[0m     msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mraw_input was called, but this frontend does not support input requests.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m   1281\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m StdinNotImplementedError(msg)\n\u001b[0;32m-> 1282\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_input_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1283\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mprompt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1284\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_parent_ident\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mshell\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1285\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_parent\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mshell\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1286\u001b[0m \u001b[43m    \u001b[49m\u001b[43mpassword\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m   1287\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/ProgramFiles/anaconda3/envs/mm/lib/python3.10/site-packages/ipykernel/kernelbase.py:1325\u001b[0m, in \u001b[0;36mKernel._input_request\u001b[0;34m(self, prompt, ident, parent, password)\u001b[0m\n\u001b[1;32m   1322\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m:\n\u001b[1;32m   1323\u001b[0m     \u001b[38;5;66;03m# re-raise KeyboardInterrupt, to truncate traceback\u001b[39;00m\n\u001b[1;32m   1324\u001b[0m     msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInterrupted by user\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m-> 1325\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m   1326\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n\u001b[1;32m   1327\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlog\u001b[38;5;241m.\u001b[39mwarning(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInvalid Message:\u001b[39m\u001b[38;5;124m\"\u001b[39m, exc_info\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: Interrupted by user"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": str(uuid.uuid4())}}\n",
    "while True:\n",
    "    user_input = input(\"User: \")\n",
    "    if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "        print(\"Goodbye!\")\n",
    "        break\n",
    "    events = graph.stream(\n",
    "        {\"messages\": [HumanMessage(content=user_input)]},\n",
    "        config,\n",
    "        stream_mode=\"values\",\n",
    "    )\n",
    "    for event in events:\n",
    "        if \"messages\" in event:\n",
    "            event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d498b085-55bc-4527-89d9-8bf7e483c3eb",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
